////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_CONTAINERS_RESOURCE_POINTER_INL__
#define __WN_CONTAINERS_RESOURCE_POINTER_INL__

#ifndef __WN_CONTAINERS_RESOURCE_POINTER_H__
    #error "WNResourcePointer.inl should never be included directly. Please include WNResourcePointer.h instead"
#endif

#include "WNCore/inc/WNTypes.h"
#include "WNCore/inc/WNAssert.h"
#include "WNCore/inc/WNAtomic.h"
#include "WNMemory/inc/WNAllocation.h"
#include "WNContainers/inc/WNResourcePointer.h"

namespace WNContainers {
    template <typename Type>
    WNResourcePointer<Type>::WNResourcePointer() :
        mResource(WN_NULL) {
    }

    template <typename Type>
    WNResourcePointer<Type>::WNResourcePointer(Type* _resource) {
        WN_DEBUG_ASSERT(_resource != WN_NULL);
        WN_DEBUG_ASSERT(_resource->GetRefs() == 0);

        _resource->AddRef();

        mResource = _resource;
    }

    template <typename Type>
    WNResourcePointer<Type>::~WNResourcePointer() {
        if (mResource != WN_NULL) {
            RemoveRef();
        }
    }

    template <typename Type>
    WNResourcePointer<Type>::WNResourcePointer(const WNResourcePointer<Type>& _resourcePointer) {
        if(_resourcePointer.mResource) {
            _resourcePointer.mResource->AddRef();
        }

        mResource = _resourcePointer.mResource;
    }

    template <typename Type>
    template <typename T2>
    WNResourcePointer<Type>::WNResourcePointer(const WNResourcePointer<T2>& _resourcePointer) {
        if(_resourcePointer.mResource) {
            _resourcePointer.mResource->AddRef();
        }

        mResource = _resourcePointer.mResource;
    }

    template <typename Type>
    Type* WNResourcePointer<Type>::operator -> () {
        return(mResource);
    }

    template <typename Type>
    const Type* WNResourcePointer<Type>::operator -> () const {
        return(mResource);
    }

    template <typename Type>
    Type& WNResourcePointer<Type>::operator * () {
        return(*mResource);
    }

    template <typename Type>
    WNResourcePointer<Type>::operator bool () const {
        return(mResource != WN_NULL);
    }


    template <typename Type>
    WN_VOID WNResourcePointer<Type>::RemoveRef() {
        if (mResource && mResource->RemoveRef()) {
            WN_DELETE(mResource);
        }

        mResource = WN_NULL;
    }

    template <typename Type>
    WNResourcePointer<Type> WNAllocateResource() {
        return(WNResourcePointer<Type>(WN_NEW(Type)));
    }

    template <typename Type, typename P1>
    WNResourcePointer<Type> WNAllocateResource1(P1 p) {
        return(WNResourcePointer<Type>(WN_NEW(Type(p))));
    }
    
    template <typename Type, typename P1, typename P2>
    WNResourcePointer<Type> WNAllocateResource2(P1 p, P2 p2) {
        return(WNResourcePointer<Type>(WN_NEW(Type(p, p2))));
    }

    template <typename Type, typename P1, typename P2, typename P3>
    WNResourcePointer<Type> WNAllocateResource3(P1 p, P2 p2, P3 p3) {
        return(WNResourcePointer<Type>(WN_NEW(Type(p, p2, p3))));
    }

    template <typename Type, typename P1, typename P2, typename P3, typename P4>
    WNResourcePointer<Type> WNAllocateResource4(P1 p, P2 p2, P3 p3, P4 p4) {
        return(WNResourcePointer<Type>(WN_NEW(Type(p, p2, p3, p4))));
    }
    
    template<typename Type>
    WNResourcePointer<Type>& WNResourcePointer<Type>::operator= (const WNResourcePointer<Type>& _resourcePointer) {
        if(_resourcePointer.mResource) {
            _resourcePointer.mResource->AddRef();
        }
        RemoveRef();
        mResource = _resourcePointer.mResource;
        return(*this);
    }

    template<typename Type>
    template<typename T2>
    WNResourcePointer<Type>& WNResourcePointer<Type>::operator= (const WNResourcePointer<T2>& _resourcePointer) {
        _resourcePointer.mResource->AddRef();
        RemoveRef();
        mResource = _resourcePointer.mResource;
        return(*this);
    }

    template <typename Type, typename Type2>
    WN_BOOL operator==(const WNResourcePointer<Type>& t1, const WNResourcePointer<Type2>& t2) {
        return(t1.mResource == t2.mResource);
    }
}


#endif // __WN_CONTAINERS_RESOURCE_POINTER_INL__